<?php
/**
 * Created by PhpStorm.
 * User: shaowei
 * Date: 2019/1/23
 * Time: 上午10:34
 */

namespace App\Http\Model;


use App\Http\Services\CommonServices;
use App\Http\Services\UserFilterServices;
use App\Http\Services\UserServices;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class RoomVote extends Base
{
    protected $table = "room_vote";
    protected $table_topic = "room_vote_topic";

    protected $redis_room_vote = "ROOM_VOTE:%s";
    protected $redis_room_vote_running = "ROOM_VOTE:RUNNING"; //正在运行的投票
    protected $redis_room_vote_to_user = "ROOM_VOTE:TO_USER:%s"; //给谁投票数据统计
    protected $redis_room_vote_to_user_time = "ROOM_VOTE:TO_USER:TIME:%s"; //给谁投票数据统计
    protected $redis_room_vote_user = "ROOM_VOTE:USER:%s"; //投票者数据统计
    protected $redis_room_vote_topic = "ROOM_VOTE:TOPIC"; //投票话题

    public function __construct(array $attributes = [])
    {
        parent::__construct($this->table, $attributes);
    }


    /**
     * 添加投票记录
     * @param $roomId int 房间ID
     * @param $type int 操作类型：1按人气票;2按钻石礼物
     * @param $voteNum int 票数
     * @param $topicId int 话题id
     * @param $voteTime int 投票时常（秒)
     * @param $userId int 发起者用户id
     * @param $toUserIds array 被投票的用户列表
     * @return array
     */
    public function startRoomVote($roomId, $type, $topicId, $voteNum, $voteTime, $userId, $toUserIds)
    {
        $voteId = md5(time() . mt_rand(0, 99999));
        $create = [
            'vote_id' => $voteId,
            'topic_id' => $topicId,
            'room_id' => $roomId,
            'user_id' => $userId,
            'type' => $type,
            'vote_time' => $voteTime,
            'vote_num' => $voteNum,
            'to_user_ids' => implode(',', $toUserIds),
            'status' => 1, //投票状态:1开始投票;2结束投票;3取消投票
            'created_at' => date("Y-m-d H:i:s"),
        ];
        $result = DB::table($this->table)->insert($create);
        if (!$result) {
            return [];
        }

        //生成缓存

        //删掉一些没用的
        unset($create['to_user_ids'], $create['created_at'], $create['user_id'], $create['status']);
        $topic = (new RoomVote())->getTopicById($topicId);
        $create['title'] = $topic['title'] ?? "";
        $create['start_time'] = time();
        $usersInfo = (new User())->get_users_by_ids($toUserIds);
        $usersInfo = array_column($usersInfo, null, 'user_id');
        $userList = [];
        foreach ($toUserIds as $uid) {
            $userList[] = [
                'user_id' => $uid,
                'nickname' => $usersInfo[$uid]['nickname'] ?? "",
                'portrait' => $usersInfo[$uid]['images'][0] ?? "",
                'vote_num' => 0,
            ];
        }
        $create['user_list'] = $userList;

        $key = sprintf($this->redis_room_vote_running);
        Redis::hset($key, $roomId, $voteId);
        $key = sprintf($this->redis_room_vote, $voteId);
        Redis::set($key, json_encode($create));
        Redis::expire($key, 86400 * 24);

        //返回数据
        return $create;
    }

    /**
     * 结束/取消一个房间投票
     * @param $voteId string 投票ID
     * @param $status int 结束类型：2结束投票,3取消投票
     * @return void
     */
    public function endRoomVote($voteId, $status)
    {
        //给谁投票的数据统计
        $key = sprintf($this->redis_room_vote_to_user, $voteId);
        $record = Redis::hgetall($key);
        $create = [
            'status' => $status,
            'finished_at' => date("Y-m-d H:i:s"),
            'vote_record' => json_encode($record),
        ];
        DB::table($this->table)->where('vote_id', $voteId)->update($create);

        //清理投票缓存
        $this->clearRoomVoteCache($voteId);
    }

    /**
     * 清空一个房间的投票数据
     * @param $voteId string 投票ID
     * @return int
     */
    public function clearRoomVoteCache($voteId)
    {
        //给谁投票的数据统计
        $key = sprintf($this->redis_room_vote, $voteId);
        $keyToUser = sprintf($this->redis_room_vote_to_user, $voteId);
        $keyToUserTime = sprintf($this->redis_room_vote_to_user_time, $voteId);
        $keyUser = sprintf($this->redis_room_vote_user, $voteId);
        $keyRunning = $this->redis_room_vote_running;
        $voteInfo = Redis::get($key);
        $voteInfo = $voteInfo ? json_decode($voteInfo, 1) : [];
        $roomId = $voteInfo['room_id'] ?? 0;

        return Redis::Pipeline(function ($pipe) use ($key, $keyRunning, $keyUser, $keyToUser, $keyToUserTime, $roomId) {
            $pipe->del($key);
            $pipe->del($keyUser);
            $pipe->del($keyToUser);
            $pipe->del($keyToUserTime);
            $pipe->hdel($keyRunning, $roomId);
        });

    }

    /**
     * 读取一个房间投票信息
     *
     * @param $voteId int 投票ID
     * @return array
     */
    public function getRoomVote($voteId)
    {
        $key = sprintf($this->redis_room_vote, $voteId);
        $data = Redis::get($key);
        if (!$data) {
            return [];
        }
        $data = json_decode($data, 1);
        return $data;
    }

    /**
     * 根据id读取投票信息
     * @return array
     */
    public function getVoteById($voteId)
    {
        $data = DB::table($this->table_topic)->where('vote_id', $voteId)->first();
        $data = self::objToArray($data);
        return $data;
    }

    /**
     * 读取一个房间投票信息
     *
     * @param $roomId int 房间ID
     * @return array
     */
    public function getRoomVoteByRoomId($roomId)
    {
        $voteId = $this->getRunningVoteId($roomId);
        if (empty($voteId)) {
            return [];
        }
        $voteInfo = $this->getRoomVote($voteId);
        return $voteInfo;
    }

    /**
     * 读取当前正在投票的房间投票ID
     * @param $roomId string 房间ID
     * @return string
     */
    public function getRunningVoteId($roomId)
    {
        $key = sprintf($this->redis_room_vote_running);
        return Redis::hget($key, $roomId);
    }

    /**
     * 用户投票
     * @param $voteId string 房间ID
     * @param $userId int 投票者ID
     * @param $toUserId int 给谁投票
     * @param $voteNum int 投票票数
     * @return int
     */
    public function pollRoomVote($voteId, $userId, $toUserId, $voteNum = 1)
    {
        //投票者数据统计
        $keyUser = sprintf($this->redis_room_vote_user, $voteId);
        //给谁投票的数据统计
        $keyToUser = sprintf($this->redis_room_vote_to_user, $voteId);
        //给谁投票的数据统计 - 投票时间
        $keyToUserTime = sprintf($this->redis_room_vote_to_user_time, $voteId);
        //读取毫秒时间戳
        list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格
        $microTime = (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000);

        $result = Redis::Pipeline(function ($pipe) use ($keyUser, $keyToUser, $keyToUserTime, $userId, $toUserId, $voteNum, $microTime) {
            $pipe->hincrby($keyUser, $userId, $voteNum);
            $pipe->hincrby($keyToUser, $toUserId, $voteNum);
            $pipe->hset($keyToUserTime, $toUserId, $microTime);
            $pipe->expire($keyUser, 86400 * 24);
            $pipe->expire($keyToUser, 86400 * 24);
            $pipe->expire($keyToUserTime, 86400 * 24);
        });

        return $result;
    }

    /**
     * 读取用户投票排行
     * @param $voteId int
     * @param $userList array
     * @return array
     */
    public function getRoomVoteRanking($voteId, $userList = [])
    {

        if (empty($userList)) {
            $voteInfo = $this->getRoomVote($voteId);
            if (empty($voteInfo)) {
                return [];
            }
            $userList = $voteInfo['user_list'];
        }

        if (empty($userList)) {
            return [];
        }

        //读取用户票数
        $ToUserMap = $this->getRoomVoteToUserMap($voteId);

        //读取投票时间
        $timeKey = sprintf($this->redis_room_vote_to_user_time, $voteId);
        $timeMap = Redis::hgetall($timeKey);

        foreach ($userList as &$uinfo) {
            $uinfo['vote_num'] = intval($ToUserMap[$uinfo['user_id']] ?? 0);
            $uinfo['time'] = $timeMap[$uinfo['user_id']] ?? 0;
            $uinfo['win'] = 0;
        }

        //如果两个人pk，则不排序，直接判胜负
        if (count($userList) == 2) {
            if ($userList[0]['vote_num'] > $userList[1]['vote_num']) {
                $userList[0]['win'] = 1;
                $userList[1]['win'] = 0;
            } elseif ($userList[0]['vote_num'] < $userList[1]['vote_num']) {
                $userList[0]['win'] = 0;
                $userList[1]['win'] = 1;
            } else {
                if ($userList[0]['time'] < $userList[1]['time']) {
                    $userList[0]['win'] = 1;
                    $userList[1]['win'] = 0;
                } else {
                    $userList[0]['win'] = 0;
                    $userList[1]['win'] = 1;
                }
            }
        } else {
            //多人pk，需要根据分数排序
            $orderBy = array_column($userList, 'vote_num');
            $orderBy2 = array_column($userList, 'time');
            array_multisort($orderBy, SORT_DESC, $orderBy2, SORT_ASC, $userList);
            $userList[0]['win'] = 1;
        }

        //删掉time字段，time字段仅排序时候，无需反给客户端
        foreach ($userList as $k => $u){
            unset($userList[$k]['time']);
        }

        return [$userList, count($ToUserMap)];
    }

    /**
     * 读取一个用户已经投票的票数
     *
     * @param $voteId string 投票ID
     * @param $userId int 投票者ID
     * @return int
     */
    public function getRoomVotePollNum($voteId, $userId)
    {
        $key = sprintf($this->redis_room_vote_user, $voteId);
        $data = Redis::hget($key, $userId);
        return empty($data) ? 0 : intval($data);
    }

    /**
     * 读取已经投票的用户列表
     *
     * @param $voteId string 投票ID
     * @return array
     */
    public function getRoomVoteToUserMap($voteId)
    {
        $key = sprintf($this->redis_room_vote_to_user, $voteId);
        $data = Redis::hgetall($key);
        return empty($data) ? [] : $data;
    }

    /**
     * 读取一个用户的当前票数
     *
     * @param $voteId string 投票ID
     * @param $userId int 用户id
     * @return array
     */
    public function getRoomVoteToUserById($voteId, $userId)
    {
        $key = sprintf($this->redis_room_vote_to_user, $voteId);
        $val = Redis::hget($key, $userId);
        return empty($val) ? 0 : $val;
    }

    /**
     * 添加话题
     * @return bool
     */
    public function addTopic($title)
    {
        $create = [
            'title' => $title,
        ];
        $result = DB::table($this->table_topic)->insert($create);
        if ($result) {
            Redis::del($this->redis_room_vote_topic);
        }
        return $result;
    }

    /**
     * 修改话题
     * @return bool
     */
    public function editTopic($id, $title)
    {
        $data = ['title' => $title];
        $result = DB::table($this->table_topic)->where(['id' => $id])->update($data);
        Redis::del($this->redis_room_vote_topic);
        return $result;
    }

    /**
     * 删除话题
     * @return bool
     */
    public function deleteTopic($id)
    {
        $data = ['will_be_deleted' => 1];
        $result = DB::table($this->table_topic)->where(['id' => $id])->update($data);
        if ($result) {
            Redis::del($this->redis_room_vote_topic);
        }
        return $result;
    }

    /**
     * 读取所有话题
     * @return array
     */
    public function getTopics()
    {
        $cache = Redis::get($this->redis_room_vote_topic);
        if ($cache) {
            return json_decode($cache, 1);
        }
        $where = ['will_be_deleted' => 0];

        $data = DB::table($this->table_topic)->where($where)->orderBy('id')->get(['id', 'title']);
        $data = self::objToArray($data);
        Redis::set($this->redis_room_vote_topic, json_encode($data, JSON_UNESCAPED_UNICODE));
        return $data;
    }

    /**
     * 根据id读取话题
     * @return array
     */
    public function getTopicById($id)
    {
        $data = DB::table($this->table_topic)->where('id', $id)->first(['id', 'title']);
        $data = self::objToArray($data);
        return $data;
    }
}
